<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> 
    <title>Raft协议实战之Redis Sentinel的选举Leader源码解析 - 天地维杰网</title>
    <meta name="keywords" content="系统架构,shutdown,不与天斗,Domino,博客,程序员,架构师,笔记,技术,分享,java,Redis">
    
    <meta property="og:title" content="Raft协议实战之Redis Sentinel的选举Leader源码解析">
    <meta property="og:site_name" content="天地维杰网">
    <meta property="og:image" content="/img/author.jpg"> 
    <meta name="title" content="Raft协议实战之Redis Sentinel的选举Leader源码解析 - 天地维杰网" />
    <meta name="description" content="Raft协议是用来解决分布式系统一致性问题的协议，在很长一段时间，Paxos被认为是解决分布式系统一致性的代名词。但是Paxos难于理解，更难以实现，诸如Google大牛们开发的分布式锁系统Chubby都遭遇了很多坑。Raft协议设计的初衷就是容易实现，保证对于普遍的人群都可以十分舒适容易的去理解。另外，它必须能够让人形成直观的认识，这样系统的构建者才能够在现实中进行必然的扩展。" />
     
    <link rel="shortcut icon" href="http://www.shutdown.cn/img/favicon.ico" />
    <link rel="apple-touch-icon" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link rel="apple-touch-icon-precomposed" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link href="http://www.shutdown.cn/js/vendor/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/main.css" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/syntax.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" id="hexo.configuration">
  var NexT = window.NexT || {};
  var CONFIG = {
    scheme: 'Pisces',
    sidebar: {"position":"left","display":"post"},
     fancybox: true, 
    motion: true
  };
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7826003325059020" crossorigin="anonymous"></script>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container one-collumn sidebar-position-left page-home  ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"> <div class="site-meta  custom-logo ">

  <div class="custom-logo-site-title">
    <a href="http://www.shutdown.cn"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">天地维杰网</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
  <p class="site-subtitle">人如秋鸿来有信，事若春梦了无痕</p>
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
    <ul id="menu" class="menu">
      
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />首页
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/redis/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-battery-full"></i> <br />Redis
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/java/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-coffee"></i> <br />java
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/linux/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-linux"></i> <br />linux
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/daily/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-bug"></i> <br />日常问题
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/spring/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-child"></i> <br />Spring和Springboot
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/mac/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-fire"></i> <br />Mac相关
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/middleware/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-gavel"></i> <br />中间件
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jiagou/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-rocket"></i> <br />架构
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/python/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-ship"></i> <br />python
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/front/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-bolt"></i> <br />前端
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jvm/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-balance-scale"></i> <br />jvm
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/c/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-battery-empty"></i> <br />c语言
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/web3/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-web3"></i> <br />web3
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/post/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />归档
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/about/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />关于
          </a>
        </li>
      
      <li class="menu-item menu-item-search">
        <a href="javascript:;" class="popup-trigger"> <i class="menu-item-icon fa fa-search fa-fw"></i> <br /> 搜索</a>
      </li>
    </ul>
    <div class="site-search">
      <div class="popup">
 <span class="search-icon fa fa-search"></span>
 <input type="text" id="local-search-input">
 <div id="local-search-result"></div>
 <span class="popup-btn-close">close</span>
</div>

    </div>
</nav>

 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
<section id="posts" class="posts-expand">
  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <header class="post-header">
      <h1 class="post-title" itemprop="name headline">
        <a class="post-title-link" href="http://www.shutdown.cn/post/raft-redis-sentinel-leader-source-code/" itemprop="url">
        Raft协议实战之Redis Sentinel的选举Leader源码解析
        </a>
      </h1>
      <div class="post-meta">
      <span class="post-time">
<span class="post-meta-item-icon">
    <i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">时间：</span>
<time itemprop="dateCreated" datetime="2016-03-22T13:04:35+08:00" content="2023-02-09">
    2023-02-09
</time>
</span> 
      

  <span class="post-category" >
  &nbsp; | &nbsp;
  <span class="post-meta-item-icon">
    <i class="fa fa-folder-o"></i>
  </span>
  <span class="post-meta-item-text">分类：</span>
  
    <span itemprop="about" itemscope itemtype="https://schema.org/Thing">
      <a href="http://www.shutdown.cn/categories/redis" itemprop="url" rel="index">
        <span itemprop="name">redis</span>
      </a>
      &nbsp; 
    </span>
  
</span>


      
 <span>
&nbsp; | &nbsp;
<span class="post-meta-item-icon">
    <i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">转载</span>
</span>

       <span>
&nbsp; | &nbsp;
<span class="post-meta-item-icon">
    <i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">阅读：</span>
<span class="leancloud-visitors-count">10285 字 ~21分钟</span>
</span>
      </div>
    </header>
    <div class="post-body" itemprop="articleBody">
    
    <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-fallback" data-lang="fallback"> 文章系转载，便于整理和分类，原文地址：https://cloud.tencent.com/developer/article/1021467</code></pre></div>
    

    

<p>Raft协议是用来解决分布式系统一致性问题的协议，在很长一段时间，Paxos被认为是解决分布式系统一致性的代名词。但是Paxos难于理解，更难以实现，诸如Google大牛们开发的分布式锁系统Chubby都遭遇了很多坑。Raft协议设计的初衷就是容易实现，保证对于普遍的人群都可以十分舒适容易的去理解。另外，它必须能够让人形成直观的认识，这样系统的构建者才能够在现实中进行必然的扩展。</p>

<p>本文从Redis Sentinel集群选择Leader的具体流程和源码分析，描述Raft协议中的选举Leader算法。关于Redis Sentinel的介绍可以参看本人的另一篇文章<a href="http://weizijun.cn/2015/04/30/redis sentinel设计与实现/">《redis sentinel设计与实现》</a>。</p>

<p>当Sentinel集群有Sentinel发现master客观下线了，就会开始故障转移流程，故障转移流程的第一步就是在Sentinel集群选择一个Leader，让Leader完成故障转移流程。</p>

<h3 id="raft协议选举流程">Raft协议选举流程</h3>

<p>描述Raft选举流程之前需要了解一些概念。</p>

<h4 id="节点的状态">节点的状态</h4>

<p>Raft协议描述的节点共有三种状态：Leader, Follower, Candidate。在系统运行正常的时候只有Leader和Follower两种状态的节点。一个Leader节点，其他的节点都是Follower。Candidate是系统运行不稳定时期的中间状态，当一个Follower对Leader的的心跳出现异常，就会转变成Candidate，Candidate会去竞选新的Leader，它会向其他节点发送竞选投票，如果大多数节点都投票给它，它就会替代原来的Leader，变成新的Leader，原来的Leader会降级成Follower。</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20230210141641.png" width = 400 /> </div>

<h4 id="term">term</h4>

<p>在分布式系统中，各个节点的时间同步是一个很大的难题，但是为了识别过期时间，时间信息又必不可少。Raft协议为了解决这个问题，引入了term（任期）的概念。Raft协议将时间切分为一个个的Term，可以认为是一种“逻辑时间”。</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20230210141657.png" width = 400 /> </div>

<h4 id="rpc">RPC</h4>

<p>Raft协议在选举阶段交互的RPC有两类：RequestVote和AppendEntries。</p>

<ul>
<li>RequestVote是用来向其他节点发送竞选投票。</li>
<li>AppendEntries是当该节点得到更多的选票后，成为Leader，向其他节点确认消息。</li>
</ul>

<h4 id="选举流程">选举流程</h4>

<p>Raft采用心跳机制触发Leader选举。系统启动后，全部节点初始化为Follower，term为0.节点如果收到了RequestVote或者AppendEntries，就会保持自己的Follower身份。如果一段时间内没收到AppendEntries消息直到选举超时，说明在该节点的超时时间内还没发现Leader，Follower就会转换成Candidate，自己开始竞选Leader。一旦转化为Candidate，该节点立即开始下面几件事情：</p>

<ul>
<li>1、增加自己的term。</li>
<li>2、启动一个新的定时器。</li>
<li>3、给自己投一票。</li>
<li>4、向所有其他节点发送RequestVote，并等待其他节点的回复。</li>
</ul>

<p>如果在这过程中收到了其他节点发送的AppendEntries，就说明已经有Leader产生，自己就转换成Follower，选举结束。</p>

<p>如果在计时器超时前，节点收到多数节点的同意投票，就转换成Leader。同时向所有其他节点发送AppendEntries，告知自己成为了Leader。</p>

<p>每个节点在一个term内只能投一票，采取先到先得的策略，Candidate前面说到已经投给了自己，Follower会投给第一个收到RequestVote的节点。每个Follower有一个计时器，在计时器超时时仍然没有接受到来自Leader的心跳RPC, 则自己转换为Candidate, 开始请求投票，就是上面的的竞选Leader步骤。</p>

<p>如果多个Candidate发起投票，每个Candidate都没拿到多数的投票（Split Vote），那么就会等到计时器超时后重新成为Candidate，重复前面竞选Leader步骤。</p>

<p>Raft协议的定时器采取随机超时时间，这是选举Leader的关键。每个节点定时器的超时时间随机设置，随机选取配置时间的1倍到2倍之间。由于随机配置，所以各个Follower同时转成Candidate的时间一般不一样，在同一个term内，先转为Candidate的节点会先发起投票，从而获得多数票。多个节点同时转换为Candidate的可能性很小。即使几个Candidate同时发起投票，在该term内有几个节点获得一样高的票数，只是这个term无法选出Leader。由于各个节点定时器的超时时间随机生成，那么最先进入下一个term的节点，将更有机会成为Leader。连续多次发生在一个term内节点获得一样高票数在理论上几率很小，实际上可以认为完全不可能发生。一般1-2个term类，Leader就会被选出来。</p>

<h3 id="sentinel的选举流程">Sentinel的选举流程</h3>

<p>Sentinel集群正常运行的时候每个节点epoch相同，当需要故障转移的时候会在集群中选出Leader执行故障转移操作。Sentinel采用了Raft协议实现了Sentinel间选举Leader的算法，不过也不完全跟论文描述的步骤一致。Sentinel集群运行过程中故障转移完成，所有Sentinel又会恢复平等。Leader仅仅是故障转移操作出现的角色。</p>

<h4 id="选举流程-1">选举流程</h4>

<ul>
<li>1、某个Sentinel认定master客观下线的节点后，该Sentinel会先看看自己有没有投过票，如果自己已经投过票给其他Sentinel了，在2倍故障转移的超时时间自己就不会成为Leader。相当于它是一个Follower。</li>
<li>2、如果该Sentinel还没投过票，那么它就成为Candidate。</li>
<li>3、和Raft协议描述的一样，成为Candidate，Sentinel需要完成几件事情

<ul>
<li>1）更新故障转移状态为start</li>
<li>2）当前epoch加1，相当于进入一个新term，在Sentinel中epoch就是Raft协议中的term。</li>
<li>3）更新自己的超时时间为当前时间随机加上一段时间，随机时间为1s内的随机毫秒数。</li>
<li>4）向其他节点发送<code>is-master-down-by-addr</code>命令请求投票。命令会带上自己的epoch。</li>
<li>5）给自己投一票，在Sentinel中，投票的方式是把自己master结构体里的leader和leader_epoch改成投给的Sentinel和它的epoch。</li>
</ul></li>
<li>4、其他Sentinel会收到Candidate的<code>is-master-down-by-addr</code>命令。如果Sentinel当前epoch和Candidate传给他的epoch一样，说明他已经把自己master结构体里的leader和leader_epoch改成其他Candidate，相当于把票投给了其他Candidate。投过票给别的Sentinel后，在当前epoch内自己就只能成为Follower。</li>
<li>5、Candidate会不断的统计自己的票数，直到他发现认同他成为Leader的票数超过一半而且超过它配置的quorum（quorum可以参考<a href="http://weizijun.cn/2015/04/30/redis sentinel 设计与实现/">《redis sentinel设计与实现》</a>）。Sentinel比Raft协议增加了quorum，这样一个Sentinel能否当选Leader还取决于它配置的quorum。</li>
<li>6、如果在一个选举时间内，Candidate没有获得超过一半且超过它配置的quorum的票数，自己的这次选举就失败了。</li>
<li>7、如果在一个epoch内，没有一个Candidate获得更多的票数。那么等待超过2倍故障转移的超时时间后，Candidate增加epoch重新投票。</li>
<li>8、如果某个Candidate获得超过一半且超过它配置的quorum的票数，那么它就成为了Leader。</li>
<li>9、与Raft协议不同，Leader并不会把自己成为Leader的消息发给其他Sentinel。其他Sentinel等待Leader从slave选出master后，检测到新的master正常工作后，就会去掉客观下线的标识，从而不需要进入故障转移流程。</li>
</ul>

<h4 id="关于sentinel超时时间的说明">关于Sentinel超时时间的说明</h4>

<p>Sentinel超时机制有几个超时概念。</p>

<ul>
<li>failover_start_time 下一选举启动的时间。默认是当前时间加上1s内的随机毫秒数</li>
<li>failover_state_change_time 故障转移中状态变更的时间。</li>
<li>failover_timeout 故障转移超时时间。默认是3分钟。</li>
<li>election_timeout 选举超时时间，是默认选举超时时间和failover_timeout的最小值。默认是10s。</li>
</ul>

<p>Follower成为Candidate后，会更新failover_start_time为当前时间加上1s内的随机毫秒数。更新failover_state_change_time为当前时间。</p>

<p>Candidate的当前时间减去failover_start_time大于election_timeout，说明Candidate还没获得足够的选票，此次epoch的选举已经超时，那么转变成Follower。需要等到<code>mstime() - failover_start_time &lt; failover_timeout*2</code>的时候才开始下一次获得成为Candidate的机会。</p>

<p>如果一个Follower把某个Candidate设为自己认为的Leader，那么它的failover_start_time会设置为当前时间加上1s内的随机毫秒数。这样它就进入了上面说的需要等到<code>mstime() - failover_start_time &lt; failover_timeout*2</code>的时候才开始下一次获得成为Candidate的机会。</p>

<p>因为每个Sentinel判断节点客观下线的时间不是同时开始的，一般都有先后，这样先开始的Sentinel就更有机会赢得更多选票，另外failover_state_change_time为1s内的随机毫秒数，这样也把各个节点的超时时间分散开来。本人尝试过很多次，Sentinel间的Leader选举过程基本上一个epoch内就完成了。</p>

<h3 id="sentinel-选举流程源码解析">Sentinel 选举流程源码解析</h3>

<p>Sentinel的选举流程的代码基本都在sentinel.c文件中，下面结合源码对Sentinel的选举流程进行说明。</p>

<h4 id="定时任务">定时任务</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">sentinelHandleRedisInstance</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>

    <span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span>

    <span style="color:#8f5902;font-style:italic">// 判断 master 是否进入SDOWN 状态
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">sentinelCheckSubjectivelyDown</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">/* Masters and slaves */</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">SRI_MASTER</span><span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#000">SRI_SLAVE</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#8f5902;font-style:italic">/* Nothing so far. */</span>
    <span style="color:#000;font-weight:bold">}</span>        

    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_MASTER</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>

        <span style="color:#8f5902;font-style:italic">// 判断 master 是否进入 ODOWN 状态
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000">sentinelCheckObjectivelyDown</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">// 查看是否需要开始故障转移
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelStartFailoverIfNeeded</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span>
            <span style="color:#8f5902;font-style:italic">// 向其他 Sentinel 发送 SENTINEL is-master-down-by-addr 命令
</span><span style="color:#8f5902;font-style:italic"></span>            <span style="color:#8f5902;font-style:italic">// 刷新其他 Sentinel 关于主服务器的状态
</span><span style="color:#8f5902;font-style:italic"></span>            <span style="color:#000">sentinelAskMasterStateToOtherSentinels</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">SENTINEL_ASK_FORCED</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">// 执行故障转移
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000">sentinelFailoverStateMachine</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">//此处调用sentinelAskMasterStateToOtherSentinels，只是为了获取其他Sentinel对于master是否存活的判断，
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#8f5902;font-style:italic">//用来下一次判断master是否进入ODOWN状态
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000">sentinelAskMasterStateToOtherSentinels</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">SENTINEL_NO_FLAGS</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>Sentinel会每隔100ms执行一次sentinelHandleRedisInstance函数。流程会检查master是否进入SDOWN状态，接着会检查master是否进入ODOWN状态，接着会查看是否需要开始故障转移，如果开始故障转移就会向其他节点拉去投票，接下来有个故障转移的状态机，根据不同的<code>failover_state</code>，决定完成不同的操作，正常的时候failover_state为SENTINEL_FAILOVER_STATE_NONE。</p>

<h4 id="向其他sentinel获取投票或者获取对master存活状态的判断结果">向其他Sentinel获取投票或者获取对master存活状态的判断结果</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">define SENTINEL_ASK_FORCED (1&lt;&lt;0)</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">sentinelAskMasterStateToOtherSentinels</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">flags</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">dictIterator</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">di</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">dictEntry</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">de</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 遍历正在监视相同 master 的所有 sentinel
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">// 向它们发送 SENTINEL is-master-down-by-addr 命令
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">di</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictGetIterator</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">sentinels</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">while</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">de</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictNext</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">di</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">ri</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictGetVal</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">de</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">// 距离该 sentinel 最后一次回复 SENTINEL master-down-by-addr 命令已经过了多久
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000">mstime_t</span> <span style="color:#000">elapsed</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">last_master_down_reply_time</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">char</span> <span style="color:#000">port</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">32</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">retval</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">/* If the master state from other sentinel is too old, we clear it. */</span>
        <span style="color:#8f5902;font-style:italic">// 如果目标 Sentinel 关于主服务器的信息已经太久没更新，那么我们清除它
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">elapsed</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">SENTINEL_ASK_PERIOD</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#ce5c00;font-weight:bold">~</span><span style="color:#000">SRI_MASTER_DOWN</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">sdsfree</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>

        <span style="color:#8f5902;font-style:italic">/* Only ask if master is down to other sentinels if:
</span><span style="color:#8f5902;font-style:italic">         *
</span><span style="color:#8f5902;font-style:italic">         * 只在以下情况满足时，才向其他 sentinel 询问主服务器是否已下线
</span><span style="color:#8f5902;font-style:italic">         *
</span><span style="color:#8f5902;font-style:italic">         * 1) We believe it is down, or there is a failover in progress.
</span><span style="color:#8f5902;font-style:italic">         *    本 sentinel 相信服务器已经下线，或者针对该主服务器的故障转移操作正在执行
</span><span style="color:#8f5902;font-style:italic">         * 2) Sentinel is connected.
</span><span style="color:#8f5902;font-style:italic">         *    目标 Sentinel 与本 Sentinel 已连接
</span><span style="color:#8f5902;font-style:italic">         * 3) We did not received the info within SENTINEL_ASK_PERIOD ms. 
</span><span style="color:#8f5902;font-style:italic">         *    当前 Sentinel 在 SENTINEL_ASK_PERIOD 毫秒内没有获得过目标 Sentinel 发来的信息
</span><span style="color:#8f5902;font-style:italic">         * 4) 条件 1 和条件 2 满足而条件 3 不满足，但是 flags 参数给定了 SENTINEL_ASK_FORCED 标识
</span><span style="color:#8f5902;font-style:italic">         */</span>
        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_S_DOWN</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">continue</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_DISCONNECTED</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">continue</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SENTINEL_ASK_FORCED</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span>
            <span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">last_master_down_reply_time</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">SENTINEL_ASK_PERIOD</span><span style="color:#000;font-weight:bold">)</span>
            <span style="color:#204a87;font-weight:bold">continue</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">/* Ask */</span>
        <span style="color:#8f5902;font-style:italic">// 发送 SENTINEL is-master-down-by-addr 命令
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000">ll2string</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">port</span><span style="color:#000;font-weight:bold">,</span><span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">port</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">port</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">retval</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">redisAsyncCommand</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">cc</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">sentinelReceiveIsMasterDownReply</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">SENTINEL is-master-down-by-addr %s %s %llu %s</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">ip</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">port</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#8f5902;font-style:italic">// 如果本 Sentinel 已经检测到 master 进入 ODOWN 
</span><span style="color:#8f5902;font-style:italic"></span>                    <span style="color:#8f5902;font-style:italic">// 并且要开始一次故障转移，那么向其他 Sentinel 发送自己的运行 ID
</span><span style="color:#8f5902;font-style:italic"></span>                    <span style="color:#8f5902;font-style:italic">// 让对方将给自己投一票（如果对方在这个纪元内还没有投票的话）
</span><span style="color:#8f5902;font-style:italic"></span>                    <span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">SENTINEL_FAILOVER_STATE_NONE</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">?</span>
                    <span style="color:#000">server</span><span style="color:#000;font-weight:bold">.</span><span style="color:#f57900">runid</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">*</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">retval</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">REDIS_OK</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">pending_commands</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000">dictReleaseIterator</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">di</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>对于每个节点，Sentinel都会确认节点是否SDOWN，对于master，还需要确认ODOWN。<code>sentinelAskMasterStateToOtherSentinels</code>方法会在master进入SDOWN或者ODOWN调用<code>sentinel is-master-down-by-addr</code>命令，SDOWN时，该命令用来获取其他Sentinel对于master的存活状态，ODOWN是用来像其他节点投票的。SDOWN时，flags是SENTINEL_NO_FLAGS，ODOWN时，flags是SENTINEL_ASK_FORCED。</p>

<h4 id="检查是否开始故障转移">检查是否开始故障转移</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">/* This function checks if there are the conditions to start the failover,
</span><span style="color:#8f5902;font-style:italic"> * that is:
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * 这个函数检查是否需要开始一次故障转移操作：
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * 1) Master must be in ODOWN condition.
</span><span style="color:#8f5902;font-style:italic"> *    主服务器已经计入 ODOWN 状态。
</span><span style="color:#8f5902;font-style:italic"> * 2) No failover already in progress.
</span><span style="color:#8f5902;font-style:italic"> *    当前没有针对同一主服务器的故障转移操作在执行。
</span><span style="color:#8f5902;font-style:italic"> * 3) No failover already attempted recently.
</span><span style="color:#8f5902;font-style:italic"> *    最近时间内，这个主服务器没有尝试过执行故障转移
</span><span style="color:#8f5902;font-style:italic"> *    （应该是为了防止频繁执行）。
</span><span style="color:#8f5902;font-style:italic"> * 
</span><span style="color:#8f5902;font-style:italic"> * We still don&#39;t know if we&#39;ll win the election so it is possible that we
</span><span style="color:#8f5902;font-style:italic"> * start the failover but that we&#39;ll not be able to act.
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * 虽然 Sentinel 可以发起一次故障转移，但因为故障转移操作是由领头 Sentinel 执行的，
</span><span style="color:#8f5902;font-style:italic"> * 所以发起故障转移的 Sentinel 不一定就是执行故障转移的 Sentinel 。
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * Return non-zero if a failover was started. 
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * 如果故障转移操作成功开始，那么函数返回非 0 值。
</span><span style="color:#8f5902;font-style:italic"> */</span>
<span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">sentinelStartFailoverIfNeeded</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#8f5902;font-style:italic">/* We can&#39;t failover if the master is not in O_DOWN state. */</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_O_DOWN</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">/* Failover already in progress? */</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_FAILOVER_IN_PROGRESS</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">/* Last failover attempt started too little time ago? */</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_start_time</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span>
        <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_timeout</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">)</span>
    <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_delay_logged</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_start_time</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">time_t</span> <span style="color:#000">clock</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_start_time</span> <span style="color:#ce5c00;font-weight:bold">+</span>
                            <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_timeout</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">/</span> <span style="color:#0000cf;font-weight:bold">1000</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">char</span> <span style="color:#000">ctimebuf</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">26</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">;</span>

            <span style="color:#000">ctime_r</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">clock</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ctimebuf</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">ctimebuf</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">24</span><span style="color:#000;font-weight:bold">]</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06"></span><span style="color:#4e9a06">&#39;</span><span style="color:#4e9a06">\0</span><span style="color:#4e9a06">&#39;</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#8f5902;font-style:italic">/* Remove newline. */</span>
            <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_delay_logged</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_start_time</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">redisLog</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span>
                <span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">Next failover delay: I will not start a failover before %s</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
                <span style="color:#000">ctimebuf</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>
        <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#000">sentinelStartFailover</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>     </code></pre></div>
<p><code>sentinelStartFailoverIfNeeded</code>方法会检查master是否为ODOWN状态。因为定时任务每次就会执行到该函数，所以还要确认故障转移状态<code>SRI_FAILOVER_IN_PROGRESS</code>是否已经开始。然后会看定时任务是否超时，只有以上条件都满足才能开始故障转移。关于定时任务是否超时，failover_start_time默认为0，它有2个地方会被修改，一个是开始故障转移后，一个是收到其他Sentinel的投票请求。failover_start_time被修改的值为 mstime()+rand()%SENTINEL_MAX_DESYNC，这就是Raft协议说的随机因子。SENTINEL_MAX_DESYNC是1000，相当于failover_start_time是当前时间加上1s内的随机值，这个保证了，不同Sentinel在超时后，下次申请Leader时间的随机。所以故障转移开始，像Raft协议描述的“<code>启动一个新的定时器</code>”，设置了failover_start_time。在投票的时候设置failover_start_time，那么先投票，再通过ODOWN和SRI_FAILOVER_IN_PROGRESS的节点，在检查定时任务是否超时的时候就无法通过，相当于是Raft协议中的<code>Follower</code>，它不会参与竞争Leader。</p>

<h4 id="成为candidate-开始竞选leader">成为Candidate，开始竞选Leader</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">/* Setup the master state to start a failover. */</span>
<span style="color:#8f5902;font-style:italic">// 设置主服务器的状态，开始一次故障转移
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">sentinelStartFailover</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">redisAssert</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_MASTER</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 更新故障转移状态
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">SENTINEL_FAILOVER_STATE_WAIT_START</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 更新主服务器状态
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">SRI_FAILOVER_IN_PROGRESS</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 更新纪元
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_epoch</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+new-epoch</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%llu</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
        <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">unsigned</span> <span style="color:#204a87;font-weight:bold">long</span> <span style="color:#204a87;font-weight:bold">long</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+try-failover</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%@</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 记录故障转移状态的变更时间
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_start_time</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">rand</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#ce5c00;font-weight:bold">%</span><span style="color:#000">SENTINEL_MAX_DESYNC</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state_change_time</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>如果Sentinel通过三重检查，进入了<code>sentinelStartFailover</code>，相当于成为了<code>Candidate</code>，它会做以下几件事情：</p>

<ul>
<li>1、把failover_state改成SENTINEL_FAILOVER_STATE_WAIT_START。</li>
<li>2、把master的状态改成故障转移中SRI_FAILOVER_IN_PROGRESS。</li>
<li>3、增加master的current_epoch，并赋值给failover_epoch。</li>
<li>4、把failover_start_time改成mstime()+rand()%SENTINEL_MAX_DESYNC。</li>
<li>5、把failover_state_change_time改成mstime()。</li>
</ul>

<p>sentinelStartFailover完成了成为Candidate的前面两步，接着要回到前面的定时任务sentinelHandleRedisInstance。因为sentinelStartFailoverIfNeeded返回了1，所以进入if流程，执行<code>sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED);</code>，开始向其他Sentinel拉票。然后就进入<code>sentinelFailoverStateMachine</code>。</p>

<h4 id="follower投票">Follower投票</h4>

<p>这里先来看下投票的源码。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">/* Vote for the sentinel with &#39;req_runid&#39; or return the old vote if already
</span><span style="color:#8f5902;font-style:italic"> * voted for the specifed &#39;req_epoch&#39; or one greater.
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * 为运行 ID 为 req_runid 的 Sentinel 投上一票，有两种额外情况可能出现：
</span><span style="color:#8f5902;font-style:italic"> * 1) 如果 Sentinel 在 req_epoch 纪元已经投过票了，那么返回之前投的票。
</span><span style="color:#8f5902;font-style:italic"> * 2) 如果 Sentinel 已经为大于 req_epoch 的纪元投过票了，那么返回更大纪元的投票。
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * If a vote is not available returns NULL, otherwise return the Sentinel
</span><span style="color:#8f5902;font-style:italic"> * runid and populate the leader_epoch with the epoch of the vote. 
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * 如果投票暂时不可用，那么返回 NULL 。
</span><span style="color:#8f5902;font-style:italic"> * 否则返回 Sentinel 的运行 ID ，并将被投票的纪元保存到 leader_epoch 指针的值里面。
</span><span style="color:#8f5902;font-style:italic"> */</span>
<span style="color:#204a87;font-weight:bold">char</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">sentinelVoteLeader</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">uint64_t</span> <span style="color:#000">req_epoch</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">char</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">req_runid</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">uint64_t</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">leader_epoch</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">req_epoch</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">req_epoch</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">sentinelFlushConfig</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+new-epoch</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%llu</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
            <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">unsigned</span> <span style="color:#204a87;font-weight:bold">long</span> <span style="color:#204a87;font-weight:bold">long</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader_epoch</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">req_epoch</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">req_epoch</span><span style="color:#000;font-weight:bold">)</span>
    <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">sdsfree</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sdsnew</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">req_runid</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader_epoch</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">sentinelFlushConfig</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+vote-for-leader</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%s %llu</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
            <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">unsigned</span> <span style="color:#204a87;font-weight:bold">long</span> <span style="color:#204a87;font-weight:bold">long</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader_epoch</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#8f5902;font-style:italic">/* If we did not voted for ourselves, set the master failover start
</span><span style="color:#8f5902;font-style:italic">         * time to now, in order to force a delay before we can start a
</span><span style="color:#8f5902;font-style:italic">         * failover for the same master. */</span>
        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">strcasecmp</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">server</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">runid</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span>
            <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_start_time</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">rand</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#ce5c00;font-weight:bold">%</span><span style="color:#000">SENTINEL_MAX_DESYNC</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">leader_epoch</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader_epoch</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#000">sdsnew</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>前面说到Candidate开始竞选后，会把当前epoch加1，这样就比Follower大1，Follower收到第一个Candidate的投票后，因为自己当前的epoch比Candidate小，所以把当前的epoch改成第一个Candidate的epoch，然后把自己认为的Leader设置成该Candidate。然后其他Candidate再发起对该Follower的投票时，由于这些Candidate的epoch与自己选出Leader的epoch一样，所以不会再改变自己认为的Leader。这样，在一个epoch内，Follower就只能投出一票，给它第一个收到投票请求的Candidate。最后有个<code>if (strcasecmp(master-&gt;leader,server.runid))</code>，这个是为了设置failover_start_time，这样Follower在当前epoch内，就无法成为Candidate了。</p>

<h4 id="sentinel执行任务的状态机">Sentinel执行任务的状态机</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">sentinelFailoverStateMachine</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">redisAssert</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_MASTER</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_FAILOVER_IN_PROGRESS</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">return</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#204a87;font-weight:bold">switch</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#204a87;font-weight:bold">case</span> <span style="color:#f57900">SENTINEL_FAILOVER_STATE_WAIT_START</span><span style="color:#000;font-weight:bold">:</span>
            <span style="color:#8f5902;font-style:italic">// 统计选票，查看是否成为leader
</span><span style="color:#8f5902;font-style:italic"></span>            <span style="color:#000">sentinelFailoverWaitStart</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">break</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">case</span> <span style="color:#f57900">SENTINEL_FAILOVER_STATE_SELECT_SLAVE</span><span style="color:#000;font-weight:bold">:</span>
            <span style="color:#8f5902;font-style:italic">// 从slave列表中选出最佳slave
</span><span style="color:#8f5902;font-style:italic"></span>            <span style="color:#000">sentinelFailoverSelectSlave</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">break</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">case</span> <span style="color:#f57900">SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE</span><span style="color:#000;font-weight:bold">:</span>
            <span style="color:#8f5902;font-style:italic">// 把选出的slave设置为master
</span><span style="color:#8f5902;font-style:italic"></span>            <span style="color:#000">sentinelFailoverSendSlaveOfNoOne</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">break</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">case</span> <span style="color:#f57900">SENTINEL_FAILOVER_STATE_WAIT_PROMOTION</span><span style="color:#000;font-weight:bold">:</span>
            <span style="color:#8f5902;font-style:italic">// 等待升级生效，如果升级超时，那么重新选择新主服务器
</span><span style="color:#8f5902;font-style:italic"></span>            <span style="color:#000">sentinelFailoverWaitPromotion</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">break</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">case</span> <span style="color:#f57900">SENTINEL_FAILOVER_STATE_RECONF_SLAVES</span><span style="color:#000;font-weight:bold">:</span>
            <span style="color:#8f5902;font-style:italic">// 向从服务器发送 SLAVEOF 命令，让它们同步新主服务器
</span><span style="color:#8f5902;font-style:italic"></span>            <span style="color:#000">sentinelFailoverReconfNextSlave</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">break</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>Sentinel处理故障转移流程是采用状态处理的模式，不同状态处理不同任务，任务完成后更新状态到下一个状态。sentinelFailoverStateMachine函数根据failover_state决定进入什么流程。在sentinelFailoverWaitStart函数里面，Leader就被选出了，其他几个状态是Leader进行故障转移的流程。</p>

<h4 id="确认自己是否成为leader">确认自己是否成为Leader</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">sentinelFailoverWaitStart</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#204a87;font-weight:bold">char</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">isleader</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">/* Check if we are the leader for the failover epoch. */</span>
    <span style="color:#8f5902;font-style:italic">// 获取给定纪元的领头 Sentinel
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">leader</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sentinelGetLeader</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_epoch</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#8f5902;font-style:italic">// 本 Sentinel 是否为领头 Sentinel ？
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">isleader</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">leader</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">strcasecmp</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">server</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">runid</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">sdsfree</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">/* If I&#39;m not the leader, and it is not a forced failover via
</span><span style="color:#8f5902;font-style:italic">     * SENTINEL FAILOVER, then I can&#39;t continue with the failover. */</span>
    <span style="color:#8f5902;font-style:italic">// 如果本 Sentinel 不是领头，并且这次故障迁移不是一次强制故障迁移操作
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">// 那么本 Sentinel 不做动作
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">isleader</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_FORCE_FAILOVER</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">election_timeout</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">SENTINEL_ELECTION_TIMEOUT</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">/* The election timeout is the MIN between SENTINEL_ELECTION_TIMEOUT
</span><span style="color:#8f5902;font-style:italic">         * and the configured failover timeout. */</span>
        <span style="color:#8f5902;font-style:italic">// 当选的时长（类似于任期）是 SENTINEL_ELECTION_TIMEOUT
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#8f5902;font-style:italic">// 和 Sentinel 设置的故障迁移时长之间的较小那个值
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">election_timeout</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_timeout</span><span style="color:#000;font-weight:bold">)</span>
            <span style="color:#000">election_timeout</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_timeout</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">/* Abort the failover if I&#39;m not the leader after some time. */</span>
        <span style="color:#8f5902;font-style:italic">// Sentinel 的当选时间已过，取消故障转移计划
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_start_time</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">election_timeout</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">-failover-abort-not-elected</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%@</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#8f5902;font-style:italic">// 取消故障转移
</span><span style="color:#8f5902;font-style:italic"></span>            <span style="color:#000">sentinelAbortFailover</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>
        <span style="color:#204a87;font-weight:bold">return</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#8f5902;font-style:italic">// 本 Sentinel 作为领头，开始执行故障迁移操作...
</span><span style="color:#8f5902;font-style:italic"></span>
    <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+elected-leader</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%@</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 进入选择从服务器状态
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">SENTINEL_FAILOVER_STATE_SELECT_SLAVE</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state_change_time</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+failover-state-select-slave</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%@</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>前面说到的<code>sentinelStartFailover</code>把failover_state设置成SENTINEL_FAILOVER_STATE_WAIT_START，于是进入<code>sentinelFailoverWaitStart</code>。</p>

<p>sentinelFailoverWaitStart会先查看leader是否已经选出。如果Leader是自己或者这是一次强制故障转移，failover_state就设置为<code>SENTINEL_FAILOVER_STATE_SELECT_SLAVE</code>。强制故障转移是通过Sentinel的<code>SENTINEL FAILOVER &lt;master-name&gt;</code>命令设置的，这里不做讨论。</p>

<p>如果自己当选Leader，就会进入下一个任务处理状态，开始故障转移流程。如果在election_timeout内还没当选为Leader，那么本次epoch内，Candidate就没有当选，需要等待failover_timeout超时，进入下一次竞选，或者本次epoch内，有Leader被选出，自己变会Follower。</p>

<h4 id="统计投票">统计投票</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">/* Scan all the Sentinels attached to this master to check if there
</span><span style="color:#8f5902;font-style:italic"> * is a leader for the specified epoch.
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * 扫描所有监视 master 的 Sentinels ，查看是否有 Sentinels 是这个纪元的领头。
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * To be a leader for a given epoch, we should have the majorify of
</span><span style="color:#8f5902;font-style:italic"> * the Sentinels we know that reported the same instance as
</span><span style="color:#8f5902;font-style:italic"> * leader for the same epoch. 
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * 要让一个 Sentinel 成为本纪元的领头，
</span><span style="color:#8f5902;font-style:italic"> * 这个 Sentinel 必须让大多数其他 Sentinel 承认它是该纪元的领头才行。
</span><span style="color:#8f5902;font-style:italic"> */</span>
<span style="color:#8f5902;font-style:italic">// 选举出 master 在指定 epoch 上的领头
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">char</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">sentinelGetLeader</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">uint64_t</span> <span style="color:#000">epoch</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">dict</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">counters</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">dictIterator</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">di</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">dictEntry</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">de</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">unsigned</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">voters</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">voters_quorum</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">char</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">myvote</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">char</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">winner</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">uint64_t</span> <span style="color:#000">leader_epoch</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">uint64_t</span> <span style="color:#000">max_votes</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#000">redisAssert</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">SRI_O_DOWN</span><span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#000">SRI_FAILOVER_IN_PROGRESS</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 统计器
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">counters</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictCreate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">leaderVotesDictType</span><span style="color:#000;font-weight:bold">,</span><span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">/* Count other sentinels votes */</span>
    <span style="color:#8f5902;font-style:italic">// 统计其他 sentinel 的主观 leader 投票
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">di</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictGetIterator</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">sentinels</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">while</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">de</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictNext</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">di</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">ri</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictGetVal</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">de</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">// 为目标 Sentinel 选出的领头 Sentinel 增加一票
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">NULL</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader_epoch</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sentinel</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">current_epoch</span><span style="color:#000;font-weight:bold">)</span>
            <span style="color:#000">sentinelLeaderIncr</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">counters</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">leader</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">// 统计投票数量
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000">voters</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000">dictReleaseIterator</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">di</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">/* Check what&#39;s the winner. For the winner to win, it needs two conditions:
</span><span style="color:#8f5902;font-style:italic">     *
</span><span style="color:#8f5902;font-style:italic">     * 选出领头 leader ，它必须满足以下两个条件：
</span><span style="color:#8f5902;font-style:italic">     *
</span><span style="color:#8f5902;font-style:italic">     * 1) Absolute majority between voters (50% + 1).
</span><span style="color:#8f5902;font-style:italic">     *    有多于一般的 Sentinel 支持
</span><span style="color:#8f5902;font-style:italic">     * 2) And anyway at least master-&gt;quorum votes. 
</span><span style="color:#8f5902;font-style:italic">     *    投票数至少要有 master-&gt;quorum 那么多
</span><span style="color:#8f5902;font-style:italic">     */</span>
    <span style="color:#000">di</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictGetIterator</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">counters</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">while</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">de</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictNext</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">di</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>

        <span style="color:#8f5902;font-style:italic">// 取出票数
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000">uint64_t</span> <span style="color:#000">votes</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictGetUnsignedIntegerVal</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">de</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">// 选出票数最大的人
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">votes</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">max_votes</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">max_votes</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">votes</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">winner</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dictGetKey</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">de</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000">dictReleaseIterator</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">di</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">/* Count this Sentinel vote:
</span><span style="color:#8f5902;font-style:italic">     * if this Sentinel did not voted yet, either vote for the most
</span><span style="color:#8f5902;font-style:italic">     * common voted sentinel, or for itself if no vote exists at all. */</span>
    <span style="color:#8f5902;font-style:italic">// 本 Sentinel 进行投票
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">// 如果 Sentinel 之前还没有进行投票，那么有两种选择：
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">// 1）如果选出了 winner （最多票数支持的 Sentinel ），那么这个 Sentinel 也投 winner 一票
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">// 2）如果没有选出 winner ，那么 Sentinel 投自己一票
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">winner</span><span style="color:#000;font-weight:bold">)</span>
        <span style="color:#000">myvote</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sentinelVoteLeader</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">epoch</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">winner</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">leader_epoch</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">else</span>
        <span style="color:#000">myvote</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sentinelVoteLeader</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">epoch</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">server</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">runid</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">leader_epoch</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 领头 Sentinel 已选出，并且领头的纪元和给定的纪元一样
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">myvote</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">leader_epoch</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">epoch</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>

        <span style="color:#8f5902;font-style:italic">// 为领头 Sentinel 增加一票（这一票来自本 Sentinel ）
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000">uint64_t</span> <span style="color:#000">votes</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sentinelLeaderIncr</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">counters</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">myvote</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#8f5902;font-style:italic">// 如果投票之后的票数比最大票数要大，那么更换领头 Sentinel
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">votes</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">max_votes</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">max_votes</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">votes</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">winner</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">myvote</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000">voters</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#8f5902;font-style:italic">/* Anyway, count me as one of the voters. */</span>

    <span style="color:#8f5902;font-style:italic">// 如果支持领头的投票数量不超过半数
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">// 并且支持票数不超过 master 配置指定的投票数量
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">// 那么这次领头选举无效
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">voters_quorum</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">voters</span><span style="color:#ce5c00;font-weight:bold">/</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">winner</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">max_votes</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">voters_quorum</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">|</span> <span style="color:#000">max_votes</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">quorum</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span>
        <span style="color:#000">winner</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#8f5902;font-style:italic">// 返回领头 Sentinel ，或者 NULL
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000">winner</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">winner</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#000">sdsnew</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">winner</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">sdsfree</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">myvote</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">dictRelease</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">counters</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">winner</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p><code>sentinelGetLeader</code>会统计所有其他Sentinel的投票结果，如果投票结果中有个Sentinel获得了超过半数且超过master的quorum，那么Leader就被选出了。</p>

<p>Candidate第一次进入sentinelGetLeader函数的时候是还没向其他Sentinel发起投票，winner为NULL，于是就会给自己投上一票，这就是前面Raft协议说到的，在开始竞选前“<code>3、给自己投一票</code>“，这样竞选前的4个步骤就全部完成了。以后再进入sentinelGetLeader就可以统计其他Sentinel的投票数目。当发现有个Sentinel的投票数据超过半数且超过quorum，就会返回该Sentinel，sentinelFailoverWaitStart会判断该Sentinel是否是自己，如果是自己，那么自己就成为了Leader，开始进行故障转移，不是自己，那么等待竞选超时，成为Follower。</p>

<h4 id="关于leader通知其他sentinel自己成为leader的说明">关于Leader通知其他Sentinel自己成为Leader的说明</h4>

<p>在Sentinel的实现里面。关于Leader发送竞选成功的消息给其他Sentinel，并没有专门的逻辑。某个Sentinel成为Leader后，他就默默的干起活。故障转移中Leader通过获取选出的slave的INFO信息，发现其确认了master身份，Leader就会修改config_epoch为最新的epoch。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">/* Process the INFO output from masters. */</span>
<span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">sentinelRefreshInstanceInfo</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">sentinelRedisInstance</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#204a87;font-weight:bold">char</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">info</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span>
    <span style="color:#8f5902;font-style:italic">/* Handle slave -&gt; master role switch. */</span>
    <span style="color:#8f5902;font-style:italic">// 处理从服务器转变为主服务器的情况
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_SLAVE</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">role</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">SRI_MASTER</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#8f5902;font-style:italic">/* If this is a promoted slave we can change state to the
</span><span style="color:#8f5902;font-style:italic">         * failover state machine. */</span>
        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">flags</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">SRI_FAILOVER_IN_PROGRESS</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span>
            <span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span>
                <span style="color:#000">SENTINEL_FAILOVER_STATE_WAIT_PROMOTION</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span>
        <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#8f5902;font-style:italic">/* Now that we are sure the slave was reconfigured as a master
</span><span style="color:#8f5902;font-style:italic">             * set the master configuration epoch to the epoch we won the
</span><span style="color:#8f5902;font-style:italic">             * election to perform this failover. This will force the other
</span><span style="color:#8f5902;font-style:italic">             * Sentinels to update their config (assuming there is not
</span><span style="color:#8f5902;font-style:italic">             * a newer one already available). */</span>
            <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">config_epoch</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_epoch</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">SENTINEL_FAILOVER_STATE_RECONF_SLAVES</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">failover_state_change_time</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mstime</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">sentinelFlushConfig</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+promoted-slave</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ri</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%@</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+failover-state-reconf-slaves</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
                <span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%@</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">sentinelCallClientReconfScript</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">SENTINEL_LEADER</span><span style="color:#000;font-weight:bold">,</span>
                <span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">start</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">sentinelForceHelloUpdateForMaster</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ri</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>
        <span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>config_epoch会通过hello频道发送给其他Sentinel。其他Sentinel发现config_epoch更新了，就会更新最新的master地址和config_epoch。这相当于Leader把当选消息告知了其他Sentinel。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">/* Process an hello message received via Pub/Sub in master or slave instance,
</span><span style="color:#8f5902;font-style:italic"> * or sent directly to this sentinel via the (fake) PUBLISH command of Sentinel.
</span><span style="color:#8f5902;font-style:italic"> *
</span><span style="color:#8f5902;font-style:italic"> * If the master name specified in the message is not known, the message is
</span><span style="color:#8f5902;font-style:italic"> * discarded. */</span>
<span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">sentinelProcessHelloMessage</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">char</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">hello</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">hello_len</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span>
        <span style="color:#8f5902;font-style:italic">/* Update master info if received configuration is newer. */</span>
        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">config_epoch</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">master_config_epoch</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">config_epoch</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">master_config_epoch</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">master_port</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">port</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">|</span>
                <span style="color:#000">strcmp</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">ip</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">token</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span>
            <span style="color:#000;font-weight:bold">{</span>
                <span style="color:#000">sentinelAddr</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">old_addr</span><span style="color:#000;font-weight:bold">;</span>

                <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+config-update-from</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">si</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%@</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
                <span style="color:#000">sentinelEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">REDIS_WARNING</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">+switch-master</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">%s %s %d %s %d</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">ip</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">port</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">token</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">master_port</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

                <span style="color:#000">old_addr</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">dupSentinelAddr</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
                <span style="color:#000">sentinelResetMasterAndChangeAddress</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">token</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">master_port</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
                <span style="color:#000">sentinelCallClientReconfScript</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">master</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">SENTINEL_OBSERVER</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06"></span><span style="color:#4e9a06">&#34;</span><span style="color:#4e9a06">start</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">,</span>
                    <span style="color:#000">old_addr</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">master</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">addr</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
                <span style="color:#000">releaseSentinelAddr</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">old_addr</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000;font-weight:bold">}</span>
        <span style="color:#000;font-weight:bold">}</span> 
    <span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000;font-weight:bold">.</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
    </div>
    <footer class="post-footer">
     
 
<div class="post-tags">     
     
    <a href="http://www.shutdown.cn/tags/redis" rel="tag" title="redis">#redis#</a>
    
    <a href="http://www.shutdown.cn/tags/sentinel" rel="tag" title="sentinel">#sentinel#</a>
    
    <a href="http://www.shutdown.cn/tags/%e9%80%89%e4%b8%be" rel="tag" title="选举">#选举#</a>
    
    <a href="http://www.shutdown.cn/tags/raft" rel="tag" title="raft">#raft#</a>
    
</div>



     <div class="post-nav">
    <div class="post-nav-next post-nav-item">
    
        <a href="http://www.shutdown.cn/post/face-exam-mysql-redis-consistency/" rel="next" title="高频面试：如何保障 MySQL 和 Redis 的数据一致性？ ">
        <i class="fa fa-chevron-left"></i> 高频面试：如何保障 MySQL 和 Redis 的数据一致性？ 
        </a>
    
    </div>

    <div class="post-nav-prev post-nav-item">
    
        <a href="http://www.shutdown.cn/post/redis-bgsave-dir-config-and-cow/" rel="prev" title="Redis 设置bgsave生成的rdb文件位置">
        Redis 设置bgsave生成的rdb文件位置 <i class="fa fa-chevron-right"></i>
        </a>
    
    </div>
</div>
      
     
     
     






    </footer>
  </article>
</section>

          </div>
        </div>
        <div class="sidebar-toggle">
  <div class="sidebar-toggle-line-wrap">
    <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
  </div>
</div>
<aside id="sidebar" class="sidebar">
  <div class="sidebar-inner">

  <ul class="sidebar-nav motion-element">
    <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
      文章目录
    </li>
    <li class="sidebar-nav-overview" data-target="site-overview">
      站点概览
    </li>
  </ul>

    <section class="site-overview sidebar-panel ">
      <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image"
        src="http://www.shutdown.cn/img/author.jpg"
        alt="不与天斗Domino" />
    <p class="site-author-name" itemprop="name">不与天斗Domino</p>
    <p class="site-description motion-element" itemprop="description"> 
        Programmer &amp; Architect</p>
</div>
      <nav class="site-state motion-element">
    <div class="site-state-item site-state-posts">
      <a href="http://www.shutdown.cn/post/">
        <span class="site-state-item-count">183</span>
        <span class="site-state-item-name">日志</span>
      </a>
    </div>
    <div class="site-state-item site-state-categories">    
        <a href="http://www.shutdown.cn/categories/">      
         
        <span class="site-state-item-count">15</span>
        
        <span class="site-state-item-name">分类</span>
        
        </a>
    </div>

    <div class="site-state-item site-state-tags">
        <a href="http://www.shutdown.cn/tags/">
         
        <span class="site-state-item-count">224</span>
        
        <span class="site-state-item-name">标签</span>
        </a>
    </div>
</nav>
      
      

      

      <div class="links-of-blogroll motion-element inline">
<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=&amp;m=0&amp;s=220&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=33&amp;bv=35" async="async"></script>
</div>

    </section>
    
<section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
    <div class="post-toc">
        <div class="post-toc-content"><nav id="TableOfContents">
<ul>
<li>
<ul>
<li>
<ul>
<li><a href="#raft协议选举流程">Raft协议选举流程</a>
<ul>
<li><a href="#节点的状态">节点的状态</a></li>
<li><a href="#term">term</a></li>
<li><a href="#rpc">RPC</a></li>
<li><a href="#选举流程">选举流程</a></li>
</ul></li>
<li><a href="#sentinel的选举流程">Sentinel的选举流程</a>
<ul>
<li><a href="#选举流程-1">选举流程</a></li>
<li><a href="#关于sentinel超时时间的说明">关于Sentinel超时时间的说明</a></li>
</ul></li>
<li><a href="#sentinel-选举流程源码解析">Sentinel 选举流程源码解析</a>
<ul>
<li><a href="#定时任务">定时任务</a></li>
<li><a href="#向其他sentinel获取投票或者获取对master存活状态的判断结果">向其他Sentinel获取投票或者获取对master存活状态的判断结果</a></li>
<li><a href="#检查是否开始故障转移">检查是否开始故障转移</a></li>
<li><a href="#成为candidate-开始竞选leader">成为Candidate，开始竞选Leader</a></li>
<li><a href="#follower投票">Follower投票</a></li>
<li><a href="#sentinel执行任务的状态机">Sentinel执行任务的状态机</a></li>
<li><a href="#确认自己是否成为leader">确认自己是否成为Leader</a></li>
<li><a href="#统计投票">统计投票</a></li>
<li><a href="#关于leader通知其他sentinel自己成为leader的说明">关于Leader通知其他Sentinel自己成为Leader的说明</a></li>
</ul></li>
</ul></li>
</ul></li>
</ul>
</nav></div>
    </div>
</section>

  </div>
</aside>

      </div>
    </main>
   
    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  <span itemprop="copyrightYear">  &copy; 
  2013 - 2023</span>
  <span class="with-love"><i class="fa fa-heart"></i></span>
  <span class="author" itemprop="copyrightHolder">天地维杰网</span>
  <span class="icp" itemprop="copyrightHolder"><a href="https://beian.miit.gov.cn/" target="_blank">京ICP备13019191号-1</a></span>
</div>
<div class="powered-by">
  Powered by - <a class="theme-link" href="http://gohugo.io" target="_blank" title="hugo" >Hugo v0.63.2</a>
</div>
<div class="theme-info">
  Theme by - <a class="theme-link" href="https://github.com/xtfly/hugo-theme-next" target="_blank"> NexT
  </a>
</div>


      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
      <span id="scrollpercent"><span>0</span>%</span>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery/index.js?v=2.1.3"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/fastclick/lib/fastclick.min.js?v=1.0.6"></script> 
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.min.js?v=1.2.1"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script src="http://www.shutdown.cn/js/vendor/ua-parser-js/dist/ua-parser.min.js?v=0.7.9"></script>

<script src="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.pack.js?v=2.1.5"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/utils.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/motion.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/affix.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/schemes/pisces.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/scrollspy.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/post-details.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/toc.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/bootstrap.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/search.js"></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js"],
    jax: ["input/TeX", "output/HTML-CSS"],
    tex2jax: {
      inlineMath: [ ['$','$'] ],
      displayMath: [ ['$$','$$'] ],
      processEscapes: true
    },
    "HTML-CSS": { fonts: ["TeX"] }
  });
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML' async></script>
</body>
</html>